﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nature.Toolkit.GMix
{
    /// <summary>
    /// Evaluates the Component resolution sequence and identifies/instantinates 
    /// the appropriate CompositionBuilder objects for each Component
    /// </summary>
    class MixExecutionPlan
    {
        #region Private Fields
        private Stack<ComponentInfo> m_resolved = new Stack<ComponentInfo>();
        private Stack<ComponentInfo> m_resolving = new Stack<ComponentInfo>();        
        #endregion

        /// <summary>
        /// Returns the resulting resolution sequence of the Expressions Components
        /// </summary>
        /// <returns></returns>
        internal ComponentInfo[] GetComponentsResolutionSequence()
        {
            return m_resolved.Reverse().ToArray();
        }

        /// <summary>
        /// Instantinates a new MixExecutionPlan object
        /// </summary>
        /// <param name="expression"></param>
        internal MixExecutionPlan(ExpressionInfo expression)
        {
            if (false == expression.Components.Any(i => i.IsRoot))
            {
                throw new ArgumentException(
                    string.Format("At least one component in the expression must be marked as the root component"), "expression");
            }
            foreach (var root in expression.Components.Where(i => i.IsRoot))
            {
                Resolve(root);
            }
            m_resolving = null;
        }


        private void Resolve(ComponentInfo component)
        {
            if (m_resolved.Contains(component)) { return; }
            if (m_resolving.Contains(component))
            {
                throw new MixCycleReferenceExceptioncs(m_resolving);
            }
            m_resolving.Push(component);                        
            foreach (ComponentInfo referedComponent in component.SubComponents )
            {
                Resolve(referedComponent);
            }
            var resolved = m_resolving.Pop();
            if (resolved != component)
            {
                throw new InvalidOperationException();
            }
            m_resolved.Push(resolved);
        }
    }
}
